﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using EFCore.BulkExtensions;
using Furion;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using YShop.Core;
using YShop.Core.Managers.Caches;

namespace YShop
{
    /// <summary>
    /// 简单CURD服务
    /// </summary>
    /// <typeparam name="TEntity">实体</typeparam>
    /// <typeparam name="TEntityDto">返回DTO</typeparam>
    /// <typeparam name="SearchDto">查询DTO</typeparam>
    /// <typeparam name="TPageSearchDto">列表查询DTO</typeparam>
    /// <typeparam name="CreateDto">创建DTO</typeparam>
    /// <typeparam name="UpdateDto">更新DTO</typeparam>
    public class CrudService<TEntity, TEntityDto, SearchDto, TPageSearchDto, CreateDto, UpdateDto> 
        : ICrudService<TEntityDto, SearchDto, TPageSearchDto, CreateDto, UpdateDto>
        where TEntity : class, IPrivateEntity,IBaseEntity, new()
        where TEntityDto: class,new()
        where TPageSearchDto : PageSearchDto
    {
        /// <summary>
        /// 仓储
        /// </summary>
        private readonly IRepository<TEntity> _repository;
        /// <summary>
        /// 创建后
        /// </summary>
        protected Action<List<CreateDto>> CreatedAction { get; set; }
        /// <summary>
        /// 修改后
        /// </summary>
        protected Action<List<UpdateDto>> UpdatedAction { get; set; }
        /// <summary>
        /// 删除后
        /// </summary>
        protected Action<string[]> DeletedAction { get; set; }

        /// <summary>
        /// 缓存实体
        /// </summary>
        public virtual List<TEntity> EntitiesCache { get; set; } = null;
        /// <summary>
        /// 缓存键
        /// </summary>
        public virtual string EntitiesCacheKey { get; set; } = null;
        protected void RemoveEntitiesCache()
        {
            if (EntitiesCacheKey == null)
                throw Oops.Oh(ErrorCodes.e9006);
            if (EntitiesCache != null)
                App.GetService<CacheManager>().Remove(EntitiesCacheKey);
        }

        public CrudService(IRepository<TEntity> repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// 创建
        /// </summary>
        /// <param name="create"></param>
        /// <returns></returns>
        public virtual async Task CreateAsync(List<CreateDto> create)
        {
            var insert = create.Adapt<List<TEntity>>();
            await _repository.InsertAsync(insert);
            CreatedAction?.Invoke(create);
            RemoveEntitiesCache();
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="keyJoin">主键</param>
        /// <returns></returns>
        [UnitOfWork]
        public virtual async Task DeleteAsync(string keyJoin)
        {
            var keys = keyJoin.Split(',');
            foreach (var key in keys)
            {

                await _repository.FakeDeleteAsync(key);
            }
            DeletedAction?.Invoke(keys);
            RemoveEntitiesCache();
        }

        /// <summary>
        /// 查询单个
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        public virtual async Task<TEntityDto> GetAsync(string id)
        {
            if(EntitiesCache != null) return EntitiesCache.Find(s => s.Id == id).Adapt<TEntityDto>();

            return (await _repository.FindAsync(id)).Adapt<TEntityDto>();
        }

        /// <summary>
        /// 查询列表
        /// </summary>
        /// <param name="pageSearchDto"></param>
        /// <returns></returns>
        [ApiDescriptionSettings(Name = "list")]
        public virtual async Task<PagedList<TEntityDto>> GetListAsync([FromQuery] TPageSearchDto pageSearchDto)
        {
            if (EntitiesCache != null)
                return EntitiesCache.ToPageList<TEntity,TEntityDto>(pageSearchDto.PageIndex, pageSearchDto.PageSize, null);

            var query = new ExpressionBuilder<TEntity>();
            var result = await _repository.Where(query.Result(), false).ToPagedListAsync(pageSearchDto.PageIndex, pageSearchDto.PageSize);
            return result.Adapt<PagedList<TEntityDto>>();
        }
        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="update"></param>
        /// <returns></returns>
        [UnitOfWork]
        public virtual async Task UpdateAsync(List<UpdateDto> update)
        {
            foreach (var u in update)
            {
                var id = u.GetType().GetProperty("Id").GetValue(u)?.ToString();
                if (!string.IsNullOrEmpty(id))
                {
                    var exist = await _repository.AnyAsync(s => s.Id == id);
                    if (!exist)
                    {
                        throw Oops.Oh(ErrorCodes.e9004);
                    }
                    await _repository.UpdateAsync(u.Adapt<TEntity>(), true);
                }
                else
                {
                    throw Oops.Oh(ErrorCodes.e9007);
                }
            }

            UpdatedAction?.Invoke(update);
            RemoveEntitiesCache();
        }
    }
}
